/*
 * ESP32 Bluetooth Audio Receiver - A2DP Application Implementation
 * 
 * Copyright (c) 2025 lianggumei
 *
 * SPDX-License-Identifier: MIT
 */

#include <string.h>

#include "bt_app_av.h"
#include "bt_app_core.h"
#include "esp_a2dp_api.h"
#include "esp_avrc_api.h"
#include "esp_bt.h"
#include "esp_bt_main.h"
#include "esp_gap_bt_api.h"
#include "esp_log.h"

static const char* kTag = "BtAppAv";

// Volume control
static uint8_t s_volume = 50;

// Bluetooth callback functions
static void bt_app_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t* param);
static void bt_app_a2dp_cb(esp_a2d_cb_event_t event, esp_a2d_cb_param_t* param);
static void bt_app_avrc_ct_cb(esp_avrc_ct_cb_event_t event, esp_avrc_ct_cb_param_t* param);
static void bt_app_avrc_tg_cb(esp_avrc_tg_cb_event_t event, esp_avrc_tg_cb_param_t* param);

// Audio data callback
static void bt_app_a2d_data_cb(const uint8_t* data, uint32_t len);

esp_err_t bt_app_init(void) {
    ESP_LOGI(kTag, "Initializing bluetooth application");
    
    // Initialize bluetooth controller
    esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
    esp_err_t ret = esp_bt_controller_init(&bt_cfg);
    if (ret != ESP_OK) {
        ESP_LOGE(kTag, "Bluetooth controller initialization failed: %s", esp_err_to_name(ret));
        return ret;
    }
    
    // Enable bluetooth controller
    ret = esp_bt_controller_enable(ESP_BT_MODE_CLASSIC_BT);
    if (ret != ESP_OK) {
        ESP_LOGE(kTag, "Bluetooth controller enable failed: %s", esp_err_to_name(ret));
        return ret;
    }
    
    // Initialize bluetooth stack
    ret = esp_bluedroid_init();
    if (ret != ESP_OK) {
        ESP_LOGE(kTag, "Bluetooth stack initialization failed: %s", esp_err_to_name(ret));
        return ret;
    }
    
    // Enable bluetooth stack
    ret = esp_bluedroid_enable();
    if (ret != ESP_OK) {
        ESP_LOGE(kTag, "Bluetooth stack enable failed: %s", esp_err_to_name(ret));
        return ret;
    }
    
    // Register callbacks
    ret = esp_bt_gap_register_callback(bt_app_gap_cb);
    if (ret != ESP_OK) {
        ESP_LOGE(kTag, "GAP callback registration failed: %s", esp_err_to_name(ret));
        return ret;
    }
    
    ret = esp_a2d_register_callback(bt_app_a2dp_cb);
    if (ret != ESP_OK) {
        ESP_LOGE(kTag, "A2DP callback registration failed: %s", esp_err_to_name(ret));
        return ret;
    }
    
    ret = esp_a2d_sink_register_data_callback(bt_app_a2d_data_cb);
    if (ret != ESP_OK) {
        ESP_LOGE(kTag, "A2DP audio data callback registration failed: %s", esp_err_to_name(ret));
        return ret;
    }
    
    ret = esp_avrc_ct_register_callback(bt_app_avrc_ct_cb);
    if (ret != ESP_OK) {
        ESP_LOGE(kTag, "AVRC controller callback registration failed: %s", esp_err_to_name(ret));
        return ret;
    }
    
    ret = esp_avrc_tg_register_callback(bt_app_avrc_tg_cb);
    if (ret != ESP_OK) {
        ESP_LOGE(kTag, "AVRC target callback registration failed: %s", esp_err_to_name(ret));
        return ret;
    }
    
    // Initialize A2DP sink
    ret = esp_a2d_sink_init();
    if (ret != ESP_OK) {
        ESP_LOGE(kTag, "A2DP Sink initialization failed: %s", esp_err_to_name(ret));
        return ret;
    }
    
    // Initialize AVRC controller
    ret = esp_avrc_ct_init();
    if (ret != ESP_OK) {
        ESP_LOGE(kTag, "AVRC controller initialization failed: %s", esp_err_to_name(ret));
        return ret;
    }
    
    // Initialize AVRC target
    ret = esp_avrc_tg_init();
    if (ret != ESP_OK) {
        ESP_LOGE(kTag, "AVRC target initialization failed: %s", esp_err_to_name(ret));
        return ret;
    }
    
    // Set device name
    ret = esp_bt_gap_set_device_name("ESP32 Bluetooth Speaker");
    if (ret != ESP_OK) {
        ESP_LOGE(kTag, "Set device name failed: %s", esp_err_to_name(ret));
        return ret;
    }
    
    // Set discoverable mode
    ret = esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE);
    if (ret != ESP_OK) {
        ESP_LOGE(kTag, "Set scan mode failed: %s", esp_err_to_name(ret));
        return ret;
    }
    
    ESP_LOGI(kTag, "Bluetooth application initialized successfully");
    return ESP_OK;
}

esp_err_t bt_app_start(void) {
    ESP_LOGI(kTag, "Starting bluetooth application");
    
    // Start A2DP sink
    esp_err_t ret = esp_a2d_sink_start();
    if (ret != ESP_OK) {
        ESP_LOGE(kTag, "A2DP Sink start failed: %s", esp_err_to_name(ret));
        return ret;
    }
    ESP_LOGI(kTag, "A2DP Sink started");
    
    // Start AVRC controller
    ret = esp_avrc_ct_start();
    if (ret != ESP_OK) {
        ESP_LOGE(kTag, "AVRC controller start failed: %s", esp_err_to_name(ret));
        return ret;
    }
    ESP_LOGI(kTag, "AVRC controller started");
    
    // Start AVRC target
    ret = esp_avrc_tg_start();
    if (ret != ESP_OK) {
        ESP_LOGE(kTag, "AVRC target start failed: %s", esp_err_to_name(ret));
        return ret;
    }
    ESP_LOGI(kTag, "AVRC target started");
    
    ESP_LOGI(kTag, "Device name: ESP32 Bluetooth Speaker");
    ESP_LOGI(kTag, "Waiting for bluetooth connection...");
    
    return ESP_OK;
}

static void bt_app_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param) {
    switch (event) {
        case ESP_BT_GAP_AUTH_CMPL_EVT:
            if (param->auth_cmpl.stat == ESP_BT_STATUS_SUCCESS) {
                ESP_LOGI(kTag, "Authentication successful: %s", param->auth_cmpl.device_name);
            } else {
                ESP_LOGE(kTag, "Authentication failed: %s", esp_err_to_name(param->auth_cmpl.stat));
        }
        break;
        case ESP_BT_GAP_PIN_REQ_EVT:
            ESP_LOGI(kTag, "PIN request");
        break;
        case ESP_BT_GAP_CFM_REQ_EVT:
            ESP_LOGI(kTag, "Confirmation request");
        break;
        case ESP_BT_GAP_KEY_NOTIF_EVT:
            ESP_LOGI(kTag, "Key notification");
        break;
        case ESP_BT_GAP_KEY_REQ_EVT:
            ESP_LOGI(kTag, "Key request");
        break;
    default:
        break;
    }
}

static void bt_app_a2dp_cb(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param) {
    switch (event) {
        case ESP_A2DP_CONNECTION_STATE_EVT: {
            esp_a2dp_connection_state_t state = param->conn_stat.state;
            switch (state) {
                case ESP_A2DP_CONNECTION_STATE_DISCONNECTED:
                    ESP_LOGI(kTag, "A2DP connection disconnected");
                    break;
                case ESP_A2DP_CONNECTION_STATE_CONNECTING:
                    ESP_LOGI(kTag, "A2DP connecting");
                    break;
                case ESP_A2DP_CONNECTION_STATE_CONNECTED:
                    ESP_LOGI(kTag, "A2DP connection successful");
                    break;
                case ESP_A2DP_CONNECTION_STATE_DISCONNECTING:
                    ESP_LOGI(kTag, "A2DP disconnecting");
        break;
                default:
        break;
            }
        break;
        }
        case ESP_A2DP_AUDIO_STATE_EVT: {
            esp_a2dp_audio_state_t state = param->audio_stat.state;
            switch (state) {
                case ESP_A2DP_AUDIO_STATE_STARTED:
                    ESP_LOGI(kTag, "A2DP audio playback started");
        break;
                case ESP_A2DP_AUDIO_STATE_STOPPED:
                    ESP_LOGI(kTag, "A2DP audio playback stopped");
        break;
                default:
        break;
            }
            break;
        }
        case ESP_A2DP_AUDIO_CFG_EVT:
            ESP_LOGI(kTag, "A2DP audio configuration event");
        break;
    default:
        break;
    }
}

static void bt_app_avrc_ct_cb(esp_avrc_ct_cb_event_t event, esp_avrc_ct_cb_param_t *param) {
    switch (event) {
        case ESP_AVRC_CT_CONNECTION_STATE_EVT:
            if (param->conn_stat.connected) {
                ESP_LOGI(kTag, "AVRC controller connected");
        } else {
                ESP_LOGI(kTag, "AVRC controller disconnected");
        }
        break;
        case ESP_AVRC_CT_CHANGE_NOTIFY_EVT:
            ESP_LOGI(kTag, "AVRC controller notification changed");
        break;
    case ESP_AVRC_CT_REMOTE_FEATURES_EVT:
            ESP_LOGI(kTag, "AVRC controller remote features");
        break;
    default:
        break;
    }
}

static void bt_app_avrc_tg_cb(esp_avrc_tg_cb_event_t event, esp_avrc_tg_cb_param_t *param) {
    switch (event) {
    case ESP_AVRC_TG_CONNECTION_STATE_EVT:
            if (param->conn_stat.connected) {
                ESP_LOGI(kTag, "AVRC target connected");
            } else {
                ESP_LOGI(kTag, "AVRC target disconnected");
            }
            break;
    case ESP_AVRC_TG_PASSTHROUGH_CMD_EVT:
            ESP_LOGI(kTag, "AVRC target passthrough command");
            break;
    case ESP_AVRC_TG_SET_ABSOLUTE_VOLUME_CMD_EVT:
            ESP_LOGI(kTag, "AVRC target set absolute volume: %d", param->set_abs_vol.volume);
            s_volume = param->set_abs_vol.volume;
            bt_app_volume_changed_cpp(s_volume);
        break;
    default:
        break;
    }
}

static void bt_app_a2d_data_cb(const uint8_t *data, uint32_t len) {
    bt_app_a2d_data_cb_cpp(data, len, s_volume);
}
